# Copyright (c) 2007 Fabio Makoto Akita
# 
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# Author: AkitaOnRails (www.akitaonrails.com)
# Support: Surgeworks LLC (www.surgeworks.com)

#
# WARNING: DO NOT EDIT THIS FILE MANUALLY - IT IS AUTOMATICALLY GENERATED BY THE PLUGIN
#
module SyncsHelper
  # There are 3 major operations in this plugin, here is the second and third majors:
  # the 'receiving' and 'sending' operations. The first brings data from the server
  # and the second sends local data to the server. In the end, both databases should
  # have the same ammount of rows for each of the replicated tables
  module Request
    private    
    # Client calls the remote down.yaml with special :count => true flag and gets data 
    # count from the server Replica.down method. This is useful to call before do_get_server_data
    # so it can be measured how much data is about to be downloaded
    def do_get_server_data_count(address, server_machine_id)
      do_get_server_data address, server_machine_id, true
    end

    # Client calls the remote down.yaml and gets data from the server Replica.down method,
    # then the client uses this data to update its own database through the internal
    # Replica.up method.
    def do_get_server_data(address, server_machine_id, count = false)
      # call the remote server sending the last time the syncing was made, so it
      # gets only the data it doesn't already have
      req = http_open_post(address, $SYNC_CONFIG[:down_uri]) do |request|
        start_id = current_client.last_synced || -1
        form_data = { 'start_id' => start_id, 'machine_id' => $SYNC_CONFIG[:unique_machine_id] }
        form_data.merge!('count' => 'true') if count
        request.set_form_data form_data
      end
      # unpacks the received YAML package and start operating through each
      # received transaction in the payload
      result ||= ActsAsReplica::Structs::SyncUpResult.new
      result.status = case req
      when Net::HTTPSuccess
        fix_model_loading
        package = YAML.load(expand(req.body))
        result.last_synced_id = package.last_synced
        result.total       = package.total.to_i

        if Replica.compare_version(package.version)
          result.errors << "Remote #{package.version} and Local #{Replica.version} versions are different."
          500
        else
          if !count && result.total > 0
            result = Replica.up(package, :machine_id => server_machine_id)
            # upon completion, stamps the latest timestam returned in the most
            # recent transaction in the payload package. Takes care to use the
            # server's date as the reference, so to avoid conflicting timezones
            # and misconfigured local clocks
            if result.last_synced_id
              current_client.last_synced = result.last_synced_id
              current_client.save
            end
          end
          200
        end
      else
        result.errors << "Unknown error #{req.to_yaml}."
        500
      end
      result
    end
    
    # Client calls the remote dump.yaml and gets a package containing a csv payload
    def do_get_server_dump(address, table_name)
      return nil unless Module.const_get(table_name)
      # call the remote server sending the table that we want to import
      req = http_open_post(address, $SYNC_CONFIG[:dump_uri]) do |request|
        start_id = current_client.last_synced || -1
        form_data = { 'table_name' => table_name }
        request.set_form_data form_data
      end

      result ||= ActsAsReplica::Structs::SyncDumpResult.new
      result.status = case req
      when Net::HTTPSuccess
        fix_model_loading
        package = YAML.load(expand(req.body))
        result.last_synced = package.last_synced
        result.total       = package.total.to_i
        
        # unpacks the received CSV package into a temporary file
        tmp_file = File.join(RAILS_ROOT, 'tmp', "#{UUID.timestamp_create().to_s}.csv")
        begin
          File.open(tmp_file, 'w') { |f| f.write package.payload } 
          if Replica.compare_version(package.version)
            result.errors << "Remote #{package.version} and Local #{Replica.version} versions are different."
            500
          else
            if result.total > 0
              # loads the CSV file into the table
              model_class = Module.const_get(table_name)
              model_class.import_from_csv(tmp_file)
              result.result_message = "Loaded #{model_class.count} rows from #{File.size(tmp_file)} csv file."
            end
            200
          end
        ensure
          File.delete(tmp_file) if File.exists?(tmp_file)
        end
      else
        result.errors << "Unknown error #{req.to_yaml}."
        500
      end      
      result
    end

    # After the successful Handshake operation, the server is able to determine
    # the last time it received new data from this particular client. So it sends
    # back this timestamp to be used here. Then the client can get the most recent
    # data to send back to it.
    def do_send_client_data(address, start_id, server_machine_id)
      # Get the latest local data
      package = Replica.down(start_id, server_machine_id, :count => false)
      return ActsAsReplica::Structs::SyncUpResult.new(:total => 0, :status => 200, 
        :last_synced_id => start_id) if package.total == 0

      # Sends the local data packaged as YAML
      req = http_open_post(address, $SYNC_CONFIG[:up_uri]) do |request|
        #request.content_type = 'application/x-yaml'
        request.content_type = 'text/plain'
        request.set_form_data('syncs' => package.to_yaml)
        logger.info "Sending data: [[[#{package.to_yaml}]]]"
      end
      # process the returned status from the server
      begin
        result = YAML.load(expand(req.body))
      rescue Exception => e
        result = ActsAsReplica::Structs::SyncUpResult.new :errors => [e], 
          :status => 500, :last_synced_id => start_id
      end
      result
    end      
  end
end